#ifndef BASE_REFLECTION_HPP
#define BASE_REFELCTION_HPP

#include <iostream>
#include <string>
#include <vector>
#include <base/defines.h>
#include <base/log.h>
#include <unordered_map>
#include <memory>
#include <functional>
#include <any>
#include <typeinfo>
#include <tuple>

namespace reflect {

namespace impl {

template <typename T>
struct RefTrait {
    static constexpr int value = 0;
};

template <typename T>
struct RefTrait<T&> {
    static constexpr int value = 1;
};

template <typename T>
struct IsConst : std::false_type {};

template <typename T>
struct IsConst<T&> : std::false_type {};

template <typename T>
struct IsConst<T*> : std::false_type {};

template <typename T>
struct IsConst<const T> : std::true_type {};

template <typename T>
struct IsConst<const T&> : std::true_type {};

template <typename T>
struct IsConst<const T*> : std::true_type {};

class ArgWarp
{
public:
    template <typename T>
    ArgWarp(T&& value) 
    {
        ref_type_ = RefTrait<T>::value;
        isconst_ = IsConst<T>::value;
        // static_assert(RefTrait<T>::value, "RefTrait<T>::value need be true");
        if (ref_type_ == 1) {
            storage_ = &value;
        } else {
            storage_ = value;
        }
    }

    template <typename T>
    T Cast() {
        using RawT = std::remove_cv_t<std::remove_reference_t<T>>;
        constexpr int castRefType = RefTrait<T>::value;
        constexpr bool castIsConstant = IsConst<T>::value;

        if constexpr (castRefType == 0) {
            if (ref_type_ == 1) {
                if (isconst_) { // want copy, self is const-ref
                    return *std::any_cast<const RawT*>(storage_);
                } else { // want copy, self is mut-ref
                    return *std::any_cast<RawT*>(storage_);
                }
            }
            // want copy, self is copy 
            return std::any_cast<RawT>(storage_);
        }  

        if (ref_type_ == 0) {
            if constexpr (castIsConstant) {
                // want const-ref, self is copy
            } else {
                // want mut-ref. self is copy
            }
            return *std::any_cast<RawT>(&storage_);
        }

        if constexpr (castIsConstant) {
            if (isconst_) {
                // want const-ref, self is const-ref
                return *std::any_cast<const RawT*>(storage_);
            } else {
                // want const-ref, self is mut-ref
                return *std::any_cast<RawT*>(storage_);
            }
        } else {
            if (isconst_) {
                throw std::runtime_error("Can not Convert const-ref to nonconst-ref");
            } 
            // want mut-ref, self is mut-ref
            return *std::any_cast<RawT*>(storage_);
        }
         
    }

private:
    int ref_type_ {0};
    bool isconst_ {false};

    std::any storage_ {};
};

template<typename... Args, size_t N, size_t... Is>
std::tuple<Args...> AsTuple(std::array<ArgWarp, N>& array, std::index_sequence<Is...>) 
{
    return std::forward_as_tuple(array[Is].template Cast<Args>()...);
}

template<typename... Args, size_t N, typename = std::enable_if_t<(N == sizeof...(Args))>>
std::tuple<Args...> AsTuple(std::array<ArgWarp, N>& array)
{
    return AsTuple<Args...>(array, std::make_index_sequence<N>());
}

}

class MemberVariable;
class MemberFunction;

class TypeDescriptor {
    friend class RawTypeDescriptorBuilder;
    public:
        const std::string& GetName() const;
        const std::vector<MemberVariable>& GetMemberVariables() const;
        const std::vector<MemberFunction>& GetMemberFunctions() const;
        MemberVariable GetMemberVariable(const std::string& name) const;
        MemberFunction GetMemberFunction(const std::string& name) const;
    protected:
        std::string name_;
        std::vector<MemberVariable> member_vars_;
        std::vector<MemberFunction> member_funcs_;
};

class MemberVariable {
    friend class RawTypeDescriptorBuilder;
    public:
        MemberVariable() = default;
       
        template<typename Class, typename Var>
        MemberVariable(Var Class::* var) 
        {
            getter_ = [var](std::any obj) -> std::any {
                return std::any_cast<const Class*>(obj) ->* var;
            };

            setter_ = [var](std::any obj, std::any val) -> void {
                auto* self = std::any_cast<Class*>(obj);
                self ->* var = std::any_cast<Var>(val); // ->* / .* 语法
            };
        }
       
        template<typename Var, typename Class>
        Var GetValue(const Class& obj) const 
        {
            return std::any_cast<Var>(getter_(&obj));
        }

        template<typename Var, typename Class>
        void SetValue(Class& obj, Var value) 
        {
            setter_(&obj, value);
        }

        const std::string& GetName() const;
    private:
        std::string name_;
        std::function<std::any(std::any)> getter_ {nullptr};
        std::function<void(std::any, std::any)> setter_ {nullptr};
};

class MemberFunction {
    friend class RawTypeDescriptorBuilder;
    public:
        MemberFunction() = default;
        const std::string& GetName() const;
        bool IsConst() const;

        template<typename Class, typename... Args>
        MemberFunction(void (Class::* func)(Args...)) 
        {
            args_num_ = sizeof...(Args);
            function_ = [this, func] (void* args_ptr) -> std::any {
                auto& args = *static_cast<std::array<impl::ArgWarp, sizeof...(Args) + 1> *>(args_ptr);
                auto tp = impl::AsTuple<Class&, Args...> (args);
                std::apply(func, tp);
                return std::any();
            };
        }

        template<typename Class, typename... Args>
        MemberFunction(void (Class::* func)(Args...) const) 
        {
            args_num_ = sizeof...(Args);
            function_ = [this, func] (void* args_ptr) -> std::any {
                auto& args = *static_cast<std::array<impl::ArgWarp, sizeof...(Args) + 1> *>(args_ptr);
                auto tp = impl::AsTuple<const Class&, Args...> (args);
                std::apply(func, tp);
                return std::any();
            };
            is_const_ = true;
        }

        template<typename Class, typename... Args, typename ReturnType>
        MemberFunction(ReturnType (Class::* func)(Args...)) 
        {
            args_num_ = sizeof...(Args);
            function_ = [this, func] (void* args_ptr) -> std::any {
                auto& args = *static_cast<std::array<impl::ArgWarp, sizeof...(Args) + 1>*>(args_ptr);
                auto tp = impl::AsTuple<Class&, Args...>(args);
                return std::apply(func, tp);
            };
        }

        template<typename Class, typename... Args, typename ReturnType>
        MemberFunction(ReturnType (Class::* func)(Args...) const) 
        {
            args_num_ = sizeof...(Args);
            function_ = [this, func] (void* args_ptr) -> std::any {
                auto& args = *static_cast<std::array<impl::ArgWarp, sizeof...(Args) + 1>*>(args_ptr);
                auto tp = impl::AsTuple<const Class&, Args...>(args); //cast参数属性
                return std::apply(func, tp);
            };
            is_const_ = true;
        }

        template<typename Class, typename... Args>
        std::any Invoke(Class& obj, Args &&... args) 
        {   
            // 检查参数个数
            if (args_num_ != sizeof...(Args)) {
                throw std::runtime_error("Mismatching number of arguments");
            }
            // if (is_const_) {
            //     auto tuple = std::make_tuple(std::reference_wrapper<const Class>(obj), args...); // make_tuple会拷贝元素, std::reference_wrapper保持引用属性，obj需要保持引用属性，其他参数外界传入std::ref/std::cref
            //     // catch 异常
            //     return function_(&tuple);
            // }
            // auto tuple = std::make_tuple(std::reference_wrapper<Class>(obj), args...); // std::ref / std::cref传递
            // // catch 异常
            std::array<impl::ArgWarp, sizeof...(Args) + 1> args_array = { // ArgWarp保存参入参数类型
                 impl::ArgWarp(obj),
                 impl::ArgWarp(std::forward<Args>(args))...
            };
            return function_(&args_array);
        }

        template<typename Class, typename... Args>
        std::any InvokeByString(Class& obj, Args &&... args)
        {
            // 检查参数个数
            if (args_num_ != sizeof...(Args)) {
                throw std::runtime_error("Mismatching number of arguments");
            }
            // 将string对象转成对应类型
            return std::any();
        }
        
    private:
        std::string name_;
        int args_num_;
        bool is_const_{false};
        std::function<std::any(void* args_ptr)> function_ {nullptr};
};

class Registry {
    public:
        static Registry& GetInstance();
        NONCOPYABLE(Registry);
        IMMOVEABLE(Registry);
        std::vector<TypeDescriptor*> GetAllTypeDescriptor() const;
        TypeDescriptor* FindTypeDescriptor(const std::string& name) const;
        void Register(std::unique_ptr<TypeDescriptor> desc);
        void Clear();
    private:
        Registry();
        static Registry instance_;
        std::unordered_map<std::string, std::unique_ptr<TypeDescriptor>> descs_;
};


class RawTypeDescriptorBuilder {
    public:
        explicit RawTypeDescriptorBuilder(const std::string& name);
        NONCOPYABLE(RawTypeDescriptorBuilder);
        IMMOVEABLE(RawTypeDescriptorBuilder);
        ~RawTypeDescriptorBuilder();

        template<typename Class, typename Var>
        void AddMemberVariable(const std::string& name, Var Class::* var) 
        { 
            MemberVariable variable(var);
            variable.name_  = name;
            desc_ -> member_vars_.push_back(variable);
        }

        template<typename Class, typename Func>
        void AddMemberFunction(const std::string& name, Func Class::* func) 
        {
            MemberFunction function(func);
            function.name_ = name;
            desc_ -> member_funcs_.push_back(function);
        }   

    private:
        std::unique_ptr<TypeDescriptor> desc_{nullptr};
};

template<typename Class>
class TypeDescriptorBuilder 
{
    public:
        explicit TypeDescriptorBuilder(const std::string& name)
            : raw_builder_(name) 
        {};

        template<typename Var>
        TypeDescriptorBuilder& AddMemberVariable(const std::string& name, Var Class::* var) 
        {
            raw_builder_.AddMemberVariable(name, var);
            return *this;
        }
        template<typename Func>
        TypeDescriptorBuilder& AddMemberFunction(const std::string& name, Func Class::* func) 
        {
            raw_builder_.AddMemberFunction(name, func);
            return *this;
        }

    private:
        RawTypeDescriptorBuilder raw_builder_;
};

template<typename Class>
TypeDescriptorBuilder<Class> AddClass(const std::string& name) 
{
    return TypeDescriptorBuilder<Class>(name);
}

std::vector<TypeDescriptor*> Get();
    
TypeDescriptor& GetByName(const std::string& name);

}
#endif